#include <stdlib.h>

#include "_gfx.h"
#include "_types.h"
#include "_window.h"
#include "_osd.h"

/* Enables or disables debug output */
#ifdef __DEBUG
#define __D(fmt, args...) fprintf(stderr, "Gfx Debug: " fmt, ## args)
#else
#define __D(fmt, args...)
#endif

#define __E(fmt, args...) fprintf(stderr, "Gfx Error: " fmt, ## args)

#define DEPTH_OFFSET 0x10           // Color difference for button borders.
#define BORDER_SIZE  5              // Size of button borders in pixels.

#define MIN 0
#define MAX 1

/* Maximum height of screen supported is 1024 pixels */
unsigned int limits[1024][2];

__u16 color_convert(__u8 r, __u8 g, __u8 b) {
    __u16 col;

    /* Scale the color values to a 565 16bit image */
    r = r * (1 << 5) / (1 << 8);
    g = g * (1 << 6) / (1 << 8);
    b = b * (1 << 5) / (1 << 8);

    col = b | (g << 5) | (r << 11);

    return col;
}

static void line_compare(unsigned int x0, unsigned int y0, unsigned int x1, unsigned int y1) {
    int dy = y1 - y0;
    int dx = x1 - x0;
    int stepx, stepy;

    if (dy < 0) {
        dy = -dy;
        stepy = -1;
    } else {
        stepy = 1;
    }
    if (dx < 0) {
        dx = -dx;
        stepx = -1;
    } else {
        stepx = 1;
    }
    dy <<= 1;
    dx <<= 1;

    if (x0 < limits[y0][MIN]) limits[y0][MIN] = x0;
    if (x0 > limits[y0][MAX]) limits[y0][MAX] = x0;

    if (dx > dy) {
        int fraction = dy - (dx >> 1);

        while (x0 != x1) {
            if (fraction >= 0) {
                y0 += stepy;
                fraction -= dx;
            }
            x0 += stepx;
            fraction += dy;
            if (x0 < limits[y0][MIN]) limits[y0][MIN] = x0;
            if (x0 > limits[y0][MAX]) limits[y0][MAX] = x0;
        }
    } else {
        int fraction = dx - (dy >> 1);

        while (y0 != y1) {
            if (fraction >= 0) {
                x0 += stepx;
                fraction -= dy;
            }
            y0 += stepy;
            fraction += dx;
            if (x0 < limits[y0][MIN]) limits[y0][MIN] = x0;
            if (x0 > limits[y0][MAX]) limits[y0][MAX] = x0;
        }
    }
}

void fill_rectangle(unsigned int x, unsigned int y, unsigned int w, unsigned int h, __u16 col, window *winp) {

    __u16 *dst = winp->rgbBuffer + y * winp->w + x;

    int i, j;

    for (j = 0; j < h; j++) {
        for (i = 0; i < w; i++) {
            dst[i] = col;
        }
        dst += winp->w;
    }
}

static void fill_triangle(unsigned int x1, unsigned int y1, unsigned int x2,
        unsigned int y2, unsigned int x3, unsigned int y3, __u16 col, window *winp) {
    unsigned int maxy, miny;
    unsigned int x, y;
    __u16 *pos;

    miny = y1;
    if (y2 < miny) miny = y2;
    if (y3 < miny) miny = y3;

    maxy = y1;
    if (y2 > maxy) maxy = y2;
    if (y3 > maxy) maxy = y3;

    /* Initialize the max and min to extreme values */
    for (y = miny; y <= maxy; y++) {
        limits[y][MIN] = 0xffffffff;
        limits[y][MAX] = 0;
    }

    /* Find max and min X values for all used Y values */
    line_compare(x1, y1, x2, y2);
    line_compare(x2, y2, x3, y3);
    line_compare(x3, y3, x1, y1);

    pos = (__u16 *) winp->rgbBuffer + miny * winp->w;

    /* Draw horizontally between max and min X and max and min Y */
    for (y = miny; y <= maxy; y++) {
        for (x = limits[y][MIN]; x <= limits[y][MAX]; x++) {
            pos[x] = col;
        }
        pos += winp->w;
    }
}

/*void draw_rectangle(_simplescreen *scrp, int x, int y, int w, int h,
        int r, int g, int b) {
    __u16 col;

    col = color_convert(r, g, b);

    fill_rectangle(x, y, w, h, col, scrp);
}*/

void gfx_render_button(button *btnp, window *winp, unsigned int r, unsigned int g, unsigned int b) {
    __u16 main_col;
    __u16 dark_col;
    __u16 light_col;
    unsigned int w = btnp->w;
    unsigned int h = btnp->h;
    unsigned int x = btnp->x;
    unsigned int y = btnp->y;
    unsigned int border = btnp->border;
    int dr = (r - DEPTH_OFFSET < 0) ? 0 : r - DEPTH_OFFSET;
    int dg = (g - DEPTH_OFFSET < 0) ? 0 : g - DEPTH_OFFSET;
    int db = (b - DEPTH_OFFSET < 0) ? 0 : b - DEPTH_OFFSET;
    int lr = (r + DEPTH_OFFSET > 255) ? 255 : r + DEPTH_OFFSET;
    int lg = (g + DEPTH_OFFSET > 255) ? 255 : g + DEPTH_OFFSET;
    int lb = (b + DEPTH_OFFSET > 255) ? 255 : b + DEPTH_OFFSET;

    main_col = color_convert(r, g, b);
    dark_col = color_convert(dr, dg, db);
    light_col = color_convert(lr, lg, lb);

    if ((x > winp->w) || (y > winp->h)) {
        return;
    }

    /* Draw the background of the button */
    if (btnp->x + btnp->w > winp->w) {
        w = winp->w - x;
    }
    if (btnp->y + btnp->h > winp->h) {
        h = winp->h - y;
    }
    fill_rectangle(x, y, w, h, main_col, winp);

    /* Draw the button depth */
    fill_rectangle(x, y + h - border, w, border, dark_col, winp);
    fill_rectangle(x, y, border, h, dark_col, winp);

    fill_rectangle(x + border, y, w - border, border, light_col, winp);
    fill_triangle(x, y, x + border, y + border, x + (border * 2), y, light_col, winp);
    fill_rectangle(x + w - border, y, border, h - border, light_col, winp);
    fill_triangle(x + w, y + h, x + w - border, y + h - border, x + w, y + h - border,
            light_col, winp);

    /* Setar opacidade maxima */
    __u8 *dst = winp->attrBuffer + (y * winp->w + x) / ATTR_PIXELS_PER_BYTE;
    int i, j;

    for (j = 0; j < h; j++) {
        for (i = 0; i < w / ATTR_PIXELS_PER_BYTE; i++) {
            dst[i] = 0x77;
        }
        dst += winp->w / ATTR_PIXELS_PER_BYTE;
    }
}

void gfx_render_box(unsigned int w, unsigned int h, unsigned int x, unsigned int y,
        unsigned int border, window *winp, int r, int g, int b) {

    __u16 main_col;
    __u16 dark_col;
    __u16 light_col;

    int dr = (r - 0x40 < 0) ? 0 : r - 0x40;
    int dg = (g - 0x40 < 0) ? 0 : g - 0x40;
    int db = (b - 0x40 < 0) ? 0 : b - 0x40;
    int lr = (r + 0x40 > 255) ? 255 : r + 0x40;
    int lg = (g + 0x40 > 255) ? 255 : g + 0x40;
    int lb = (b + 0x40 > 255) ? 255 : b + 0x40;

    main_col = color_convert(r, g, b);
    dark_col = color_convert(dr, dg, db);
    light_col = color_convert(lr, lg, lb);

    /* Desenha o fundo do box. */
    if (x + w > winp->w) {
        w = winp->w - x;
    }
    if (y + h > winp->h) {
        h = winp->h - y;
    }
    fill_rectangle(x, y, w, h, main_col, winp);

    /* Desenha a profundidade do box. */
    fill_rectangle(x, y + h - border, w, border, dark_col, winp);
    fill_rectangle(x, y, border, h, dark_col, winp);

    fill_rectangle(x + border, y, w - border, border, light_col, winp);
    fill_triangle(x, y, x + border, y + border, x + (border * 2), y, light_col, winp);
    fill_rectangle(x + w - border, y, border, h - border, light_col, winp);
    fill_triangle(x + w, y + h, x + w - border, y + h - border, x + w, y + h - border, light_col, winp);

    /* Setar opacidade maxima */
    __u8 *dst = winp->attrBuffer + (y * winp->w + x) / ATTR_PIXELS_PER_BYTE;
    int i, j;

    for (j = 0; j < h; j++) {
        for (i = 0; i < w / ATTR_PIXELS_PER_BYTE; i++) {
            dst[i] = 0x77;
        }
        dst += winp->w / ATTR_PIXELS_PER_BYTE;
    }
}
